/*
Copyright (C) 2015 Marien Raat

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <islandHeightmapGenerator.h>

#include <normalizer.h>

IslandHeightmapGenerator::IslandHeightmapGenerator(std::mt19937 *mt)
{
    dsGenerator = new DiamondSquareGenerator(mt);
    firstMap = true;
}

void IslandHeightmapGenerator::initializeMap(
    int width,
    int height,
    double *modifiers,
    bool overwriteOldMap)
{
    this->width = width;
    this->height = height;
    if (firstMap || !overwriteOldMap) {
        islandMap = new double[width * height];
    } else if (previousWidth != width ||
               previousHeight != height) {
        delete islandMap;
        islandMap = new double[width * height];
    }
    firstMap = false;
    previousWidth = width;
    previousHeight = height;

    for (int i = 0; i < width * height; i++) islandMap[i] = 0.0;

    diamondSquareSize = (int) pow(2.0, ceil
                                  (log(std::max(width, height) - 1) /
                                   log(2.0))) + 1;

    DiamondSquareMapSettings dsSettings;
    dsSettings.size = diamondSquareSize;
    dsSettings.h = 0.0; // Irrelevant with the used modifier function
    dsSettings.mapType = MAPTYPE_LAND; // This class uses a different
                                       // method to transform a land
                                       // map to an island.
    dsSettings.overwriteOldMap = true;
    dsSettings.modifierFunction = this->modifierFunction;
    dsSettings.modifiers = modifiers;

    dsGenerator->createNewMap(dsSettings);
}

void IslandHeightmapGenerator::pass()
{
    dsGenerator->pass();

    updateIslandMap();
}

void IslandHeightmapGenerator::finishMap()
{
    dsGenerator->finishMap();

    updateIslandMap();
}

void IslandHeightmapGenerator::createNewMap(
    int width,
    int height,
    double *modifiers,
    bool overwriteOldMap)
{
    initializeMap(width, height, modifiers, overwriteOldMap);

    finishMap();
}

bool IslandHeightmapGenerator::isMapDone()
{
    return dsGenerator->isMapDone();
}

double *IslandHeightmapGenerator::getRawDiamondSquareMap()
{
    return dsGenerator->map;
}

int IslandHeightmapGenerator::getCurrentPassSize()
{
    return dsGenerator->currentPassSettings.passSize;
}

void IslandHeightmapGenerator::updateIslandMap()
{
    // Copy the map
    for (int i = 0; i < width * height; i++) {
        int x = i % width, y = i / width;
        this->islandMap[i] = dsGenerator->map
            [x + y * diamondSquareSize];
    }
    // Normalize the map
    Normalizer<double>::normalize(islandMap, islandMap,
                                  width * height, 0.0, 1.0);
    // Apply a radial mask
    for (int i = 0; i < width * height; i++) {
        int x = i % width, y = i / width;
        double distanceFromCenter = sqrt(
            pow((((double) x) - (((double) width) / 2.0)), 2.0) +
            pow((((double) y) - (((double) height) / 2.0)), 2.0)),
            maxDistance = sqrt(pow(((double) width) / 2.0, 2.0) +
                               pow(((double) height) / 2.0, 2.0));
        islandMap[i] *= (1.0 - (distanceFromCenter
                                / maxDistance));
    }
    // Re-normalize the map
    Normalizer<double>::normalize(islandMap, islandMap,
                                  width * height, 0.0, 1.0);
    for (int i = 0; i < width * height; i++) {
        islandMap[i] = modifiedHeightValue(islandMap[i]);
    }
}

double IslandHeightmapGenerator::modifiedHeightValue(double h)
{
    return h > 0.45 ? 0.45 + 0.55 * pow((h - 0.45) / 0.55, 1.17) : h;
}

double IslandHeightmapGenerator::modifierFunction(
    int pass,
    double h,
    double *modifiers)
{
    return modifiers[pass];
}
